package com.mspbots.attendance.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mspbots.api.client.TeamsClient;
import com.mspbots.attendance.config.AttendanceWebsocketServer;
import com.mspbots.attendance.controller.TenantAttendanceController;
import com.mspbots.attendance.mapper.TenantAttendanceMapper;
import com.mspbots.attendance.model.*;
import com.mspbots.attendance.service.TenantAttendanceService;
import com.mspbots.common.TimeZoneUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * @author aff
 */
@Log4j2
@Service
public class TenantAttendanceServiceImpl extends ServiceImpl<TenantAttendanceMapper, TenantAttendance>
        implements TenantAttendanceService {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private AttendanceWebsocketServer attendanceWebsocketServer;

    @Autowired
    private TeamsClient teamsClient;


    @Override
    public LogResult log(TenantAttendance tenantAttendance) {
        LogResult logResult = new LogResult();
        logResult.setErrorType(0);
        Users user = this.baseMapper.selectUserInfo(tenantAttendance.getUserId());
        if (ObjectUtils.isEmpty(user) || !user.getEnabled()) {
            log.info("not find user or user enabled ,userid:{}", tenantAttendance.getUserId());
            logResult.setResult(false);
            logResult.setErrorType(3);
            return logResult;
        }

        List<TenantAttendance> lasts = this.findCurrentDayLog(tenantAttendance);


        tenantAttendance.setCreateTime(TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime());
        if (ObjectUtils.isEmpty(tenantAttendance.getFromType())) {
            tenantAttendance.setFromType("Mspbots");
        }

        TenantAttendance tzOffset = getTzOffset(tenantAttendance);

        tenantAttendance.setTimezone(tzOffset.getTimezone());
        tenantAttendance.setTimezoneOffset(tzOffset.getTimezoneOffset());

        tenantAttendance.setLogTime(TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime());

        //action:in  out
        //subAction:lunch lunchback break breakback
        if (ObjectUtils.isEmpty(tenantAttendance.getAction())) {
            log.debug("SubAction:::" + tenantAttendance.getSubAction());
            if (tenantAttendance.getSubAction().toLowerCase().trim().contains("back")) {
                tenantAttendance.setAction("in");
                tenantAttendance.setPaid(true);
            } else {
                tenantAttendance.setAction("out");
                tenantAttendance.setPaid(false);
            }
        } else {
            log.debug("Action:::" + tenantAttendance.getAction());
            String action = tenantAttendance.getAction().toLowerCase().trim();
            if (action.contains("in")) {
                tenantAttendance.setPaid(true);
            } else {
                tenantAttendance.setPaid(false);
            }


            String timeStr = tenantAttendance.getTimeStr();
            if (!ObjectUtils.isEmpty(timeStr)) {
                Integer hour = 0;
                Integer minuts = 0;
                if (timeStr.contains("am") || timeStr.contains("pm")) {
                    if (timeStr.contains("am")) {
                        String[] arr = timeStr.replace("am", "").split(":");
                        hour = Integer.parseInt(arr[0].trim());
                        if (timeStr.contains(":")) {
                            minuts = Integer.parseInt(arr[1].trim());
                        }
                    }
                    if (timeStr.contains("pm")) {
                        String[] arr = timeStr.replace("pm", "").split(":");

                        hour = Integer.parseInt(arr[0].trim());
                        if (hour < 12) {
                            hour = hour + 12;
                        }
                        if (timeStr.contains(":")) {
                            minuts = Integer.parseInt(arr[1].trim());
                        }
                    }
                } else {
                    String[] arr = timeStr.split(":");
                    hour = Integer.parseInt(arr[0]);
                    if (timeStr.contains(":")) {
                        minuts = Integer.parseInt(arr[1]);
                    }
                }

                LocalDateTime local = ZonedDateTime.now(ZoneId.of(tenantAttendance.getTimezoneOffset())).toLocalDateTime();

                LocalDateTime logTime1 = LocalDateTime.of(LocalDate.of(local.getYear(), local.getMonth().getValue(), local.getDayOfMonth()), LocalTime.of(hour, minuts, 0, 0));
                //MM/dd HH:mm
                logResult.setTimeStr(logTime1.format(DateTimeFormatter.ofPattern("HH:mm")));
                log.info("setTimeStr:" + logResult.getTimeStr());
                log.info("logTime1" + logTime1);

                ZonedDateTime logTime2 = ZonedDateTime.of(logTime1, ZoneId.of(tenantAttendance.getTimezoneOffset()));
                log.info("logTime2:" + logTime2);

                LocalDateTime logTime3 = TimeZoneUtils.zoneToUtc(logTime2.toLocalDateTime(), tenantAttendance.getTimezoneOffset()).toLocalDateTime();
                log.info("logTime3:" + logTime3);
                tenantAttendance.setLogTime(logTime3);

//                LocalDateTime currentTime = ZonedDateTime.now(ZoneId.of(tenantAttendance.getTimezoneOffset())).toLocalDateTime();
                LocalDateTime currentTime = LocalDateTime.now();
                log.info("currentTime:" + currentTime);
                log.info("logTime3:" + logTime3);
                if (logTime3.isAfter(currentTime)) {
                    logResult.setResult(false);
                    logResult.setErrorType(1);
                    return logResult;
                }

            }
        }

        if (!isLog(lasts, tenantAttendance, logResult)) {
            log.debug("can not log tenantID:{}  , userId:{}", tenantAttendance.getTenantId(), tenantAttendance.getUserId());
            logResult.setResult(false);
            logResult.setErrorType(2);
            return logResult;
        }


        tenantAttendance.setTeamsUserId(this.baseMapper.selectTeamUser(tenantAttendance));
        List<String> teamNames = this.baseMapper.selectTenantUserExtend(tenantAttendance);
        Set<String> set = dealWithTeam(teamNames);
        String teamStr = String.valueOf(new ArrayList<String>(set));
        if (ObjectUtils.isEmpty(teamStr) || teamStr.equals("[]")) {
            log.info("have not query team info tenantID:{} , userId:{}", tenantAttendance.getTenantId(), tenantAttendance.getUserId());
            tenantAttendance.setTeam("Ungrouped");
        } else {
            tenantAttendance.setTeam(teamStr);
        }


        String firstName = ObjectUtils.isEmpty(user.getFirstName()) ? "" : user.getFirstName();
        String lastName = ObjectUtils.isEmpty(user.getLastName()) ? "" : user.getLastName();
        String name = firstName + lastName;
        if (name.length() == 0) {
            log.info("name.length() == 0");
            name = user.getUsername().split("@")[0].replace(".", "");
        }
        tenantAttendance.setUsername(name);


        if (!CollectionUtils.isEmpty(lasts)) {
            logResult.setLastAction(lasts.get(0).getAction());
            TenantAttendance lastAttendance = lasts.get(0);
            lastAttendance.setDuration(Duration.between(lastAttendance.getLogTime(), LocalDateTime.now()).toMinutes());
            this.saveOrUpdate(lastAttendance);
        }

        if (tenantAttendance.getAction().equalsIgnoreCase("out")
                && !ObjectUtils.isEmpty(tenantAttendance.getTeamsUserId())) {
            Map<String, Object> paramCommand = new HashMap<String, Object>();
            paramCommand.put("teamsUserId", tenantAttendance.getTeamsUserId());
            paramCommand.put("message", "Stats");
            paramCommand.put("tenantId", tenantAttendance.getTenantId());
            paramCommand.put("sync", false);
            String subActionStr = ObjectUtils.isEmpty(tenantAttendance.getSubAction()) ? "" : tenantAttendance.getSubAction();
            paramCommand.put("fromType", "log action:" + tenantAttendance.getAction() + " subAction:" + subActionStr);
            JsonNode commandResult = teamsClient.post("/message/command", paramCommand).block();
            log.info("attendance log teamsClient:commandResult: {}", commandResult);
        }

        boolean result = this.save(tenantAttendance);
        attendanceWebsocketServer.sendAll("result");

        logResult.setResult(result);
        logResult.setLogTime(tenantAttendance.getLogTime());
        return logResult;
    }


    private TenantAttendance getTzOffset(TenantAttendance tenantAttendance) {
        TenantAttendance result = new TenantAttendance();
        String timezone = "UTC";
        String offset = "+00:00";
        Users user = this.baseMapper.selectUserInfo(tenantAttendance.getUserId());
        if (!ObjectUtils.isEmpty(user.getTimezoneFull())) {
            timezone = user.getTimezoneFull();
            offset = user.getTimezoneOffset();
        } else {
            Tenant tenant = this.baseMapper.selectTenantInfo(user.getTenantId());
            if (!ObjectUtils.isEmpty(tenant.getTimezoneFull())) {
                timezone = tenant.getTimezoneFull();
                offset = tenant.getTimezoneOffset();
            }
        }

        result.setTimezone(timezone);
        result.setTimezoneOffset(offset);

//        Long tenantUserId = this.baseMapper.selectUserMappingByUserId(tenantAttendance.getUserId());
//        TenantAttendance timezone = null;
//        if(!ObjectUtils.isEmpty(tenantUserId)){
//            timezone = this.baseMapper.selectUserTimezone(tenantUserId);
//        }
//
//        if(ObjectUtils.isEmpty(timezone)){
//            log.debug("tenant user timezone config is null:::tenantID:{}, userId:{}",tenantAttendance.getTenantId(),tenantAttendance.getUserId());
//            TenantAttendance tenantTimezone = this.baseMapper.selectTenantTimezone(tenantAttendance.getTenantId());
//            if(!ObjectUtils.isEmpty(tenantTimezone)){
//                result.setTimezone(tenantTimezone.getTimezone());
//                result.setTimezoneOffset(tenantTimezone.getTimezoneOffset());
//            }else{
//                log.debug("tenant timezone config is null,use UTC:::tenantID:{}, userId:{}",tenantAttendance.getTenantId(),tenantAttendance.getUserId());
//                result.setTimezone("UTC");
//                result.setTimezoneOffset("+00:00");
//            }
//        }else{
//
//            log.debug("tenant timezone ::tenantID:{}, userId:{}",tenantAttendance.getTenantId(),tenantAttendance.getUserId());
//            result.setTimezone(timezone.getTimezone());
//            result.setTimezoneOffset(timezone.getTimezoneOffset());
//        }
        return result;
    }


    private Boolean isLog(List<TenantAttendance> lasts, TenantAttendance currentLog, LogResult logResult) {
        if (!CollectionUtils.isEmpty(lasts)) {
            TenantAttendance lastLog = lasts.get(0);
            String lastAction = lastLog.getAction().trim().toLowerCase();
            String lastSubAction = lastLog.getSubAction();
            String curAction = currentLog.getAction().trim().toLowerCase();
            String curSubAction = currentLog.getSubAction();
            if (curAction.equals("in")) {
                if (!ObjectUtils.isEmpty(curSubAction)) {
                    //in back
                    if (lastAction.equals("out")
                            && !ObjectUtils.isEmpty(lastSubAction)
                            && (lastSubAction.trim().toLowerCase().equals("break") || lastSubAction.trim().toLowerCase().equals("lunch"))) {
                        log.debug("isLog:last is : out break/lunch");
                        return true;
                    } else {
                        log.debug("isLog:last is :in back:in:out");
                        logResult.setLastAction(!ObjectUtils.isEmpty(lastSubAction) ? lastSubAction.trim().toLowerCase() : lastAction);
                        logResult.setFromType(lastLog.getFromType());
                        return false;
                    }
                } else {
                    //in
                    if (lastAction.equals("out") && ObjectUtils.isEmpty(lastSubAction)) {
                        log.debug("isLog:last is :out");
                        return true;
                    } else {
                        log.info("isLog:last is :in:out break/lunch:out");
                        logResult.setLastAction(!ObjectUtils.isEmpty(lastSubAction) ? lastSubAction.trim().toLowerCase() : lastAction);
                        logResult.setFromType(lastLog.getFromType());
                        return false;
                    }
                }
            } else {
                if (lastAction.equals("in")) {
                    log.debug("isLog:last is :in back:in");
                    return true;
                } else {
                    log.debug("isLog:last is :out:out break/lunch:in");
                    logResult.setLastAction(!ObjectUtils.isEmpty(lastSubAction) ? lastSubAction.trim().toLowerCase() : lastAction);
                    logResult.setFromType(lastLog.getFromType());
                    return false;
                }
            }
        } else {
            log.debug("isLog::lasts is null");
            return true;
        }
    }

    @Override
    public List<TenantAttendance> findCurrentDayLog(TenantAttendance tenantAttendance) {
        List<TenantAttendance> list = this.baseMapper.selectCurrentDayLog(tenantAttendance);
        list.parallelStream().forEach(t -> {
            if (!ObjectUtils.isEmpty(t.getDuration())) {
                t.setEndTime(t.getLogTime().plusMinutes(t.getDuration()));
            }
            TenantAttendance tzObj = this.getTzOffset(t);
            t.setTimezone(tzObj.getTimezone());
            t.setTimezoneOffset(tzObj.getTimezoneOffset());
        });
        return list;
    }

    @Override
    public List<String> findTeam(TenantAttendance tenantAttendance) {
        List<String> list = this.baseMapper.selectAllTeam(tenantAttendance);
        Set<String> set = dealWithTeam(list);
        list = new ArrayList<>(set);
        list.add("Ungrouped");
        list.sort(Comparator.comparing(String::toString));
        return list;
    }


    public Set<String> dealWithTeam(List<String> list) {
        Set<String> set = new HashSet<>();
        list.stream().forEach(s -> {
            JsonNode jsonArr;
            try {
                if (!ObjectUtils.isEmpty(s)) {
                    jsonArr = this.objectMapper.readTree(s);
                } else {
                    jsonArr = null;
                }
            } catch (JsonProcessingException e) {
                jsonArr = null;
                log.error("JsonProcessingException::" + e.getMessage());
                e.printStackTrace();
            }
            if (!ObjectUtils.isEmpty(jsonArr)) {
                for (JsonNode json : jsonArr) {
                    if (json.has("name")) {
                        set.add(json.get("name").asText());
                    }
                }
            }
        });
        return set;
    }

    @Override
    public IPage<TenantAttendance> findByParam(Page<TenantAttendance> page, TenantAttendanceController.TenantAttendanceParams params) {
        if (!ObjectUtils.isEmpty(params.getAction())) {
            if (params.getAction().toLowerCase().equals("break") || params.getAction().toLowerCase().equals("lunch") || params.getAction().toLowerCase().equals("back")) {
                params.setSubAction(params.getAction());
                params.setAction(null);
            }
        }
        return this.baseMapper.selectByParamPage(page, params);
    }

    @Override
    public Object findExecl(TenantAttendanceController.TenantAttendanceParams params, HttpServletResponse response) {

        //declare a workbook
        HSSFWorkbook workbook = new HSSFWorkbook();

        //Generate a form and set the form name to "Record Form"
        HSSFSheet sheet = workbook.createSheet("log record");

        //Set table column width to 10 bytes
        sheet.setDefaultColumnWidth(10);
        //create a title display style
        HSSFCellStyle headerStyle = workbook.createCellStyle();
        headerStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
//        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND.);
        //Create first row header
        HSSFRow headrow = sheet.createRow(0);
        String[] header = {"Team", "Member", "Action", "Time Stamp", "Approve Time", "Duration", "status", "Comments"};

        //Iterating through table headers
        for (int i = 0; i < header.length; i++) {
            //Create a cell
            HSSFCell cell = headrow.createCell(i);

            //Create a content object
            HSSFRichTextString text = new HSSFRichTextString(header[i]);

            //Write the text content of a content object into a cell
            cell.setCellValue(text);
            cell.setCellStyle(headerStyle);
        }

        //Get all record
        List<TenantAttendance> list = this.baseMapper.selectByParam(params);
        log.debug("list size:" + list.size());
        for (int i = 0; i < list.size(); i++) {
            log.debug("id::" + list.get(i).getId());
            //Create a line
            HSSFRow row1 = sheet.createRow(i + 1);
            //Create and assign the first column
            row1.createCell(0).setCellValue(new HSSFRichTextString(list.get(i).getTeam()));
            //The second column is created and assigned
            row1.createCell(1).setCellValue(new HSSFRichTextString(list.get(i).getUsername()));
            //The third column is created and assigned
            log.debug("list.get(i).getAction():" + list.get(i).getAction());
            row1.createCell(2).setCellValue(new HSSFRichTextString(list.get(i).getAction()));

            //Column 5 is created and assigned
            log.debug("list.get(i).getLogTime():" + list.get(i).getLogTime());
            log.debug("list.get(i).getTimezoneOffset():" + list.get(i).getTimezoneOffset());
            row1.createCell(3).setCellValue(new HSSFRichTextString(TimeZoneUtils.offset(list.get(i).getLogTime(), list.get(i).getTimezoneOffset()).format(DateTimeFormatter.ofPattern("YYYY/MM/dd HH:mm"))));
            log.debug("list.get(i).getApproveTime():" + list.get(i).getApproveTime());
            log.debug("list.get(i).getApproveTime():" + list.get(i).getApproveTime());
            row1.createCell(4).setCellValue(new HSSFRichTextString(ObjectUtils.isEmpty(list.get(i).getLogTimeUpdated()) ?
                    "NA" : TimeZoneUtils.offset(list.get(i).getLogTimeUpdated(), list.get(i).getTimezoneOffset()).format(DateTimeFormatter.ofPattern("YYYY/MM/dd HH:mm"))));
            //Column 6 is created and assigned
            row1.createCell(5).setCellValue(new HSSFRichTextString(null == list.get(i).getDuration() ? "" : list.get(i).getDuration().toString()));
            //Column 7 is created and assigned
            row1.createCell(6).setCellValue(new HSSFRichTextString(list.get(i).getStatus()));
            row1.createCell(7).setCellValue(new HSSFRichTextString(list.get(i).getComments()));
        }


        // Ready to output the Excel output stream to the page via response
        //Octal output stream
        response.setContentType("application/octet-stream");

        //After that you can set the name of the exported Excel, record.xls
        response.setHeader("Content-disposition", "attachment;filename=log record.xls");

        //Flush buffer
        try {
            response.flushBuffer();
            //workbook writes Excel to the output stream of the response for page download
            workbook.write(response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public List<TenantAttendance> findTenantLog(TenantAttendanceController.TenantAttendanceParams tenantAttendanceParams) {
//        List<TenantAttendance> list = this.baseMapper.selectTenantLog(tenantAttendanceParams);
        log.debug("findTenantLog param: TenantId:{} userid:{}", tenantAttendanceParams.getTenantId(), tenantAttendanceParams.getUserIds());
        log.debug("findTenantLog TenantId :{} user start:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());
        List<TenantAttendance> list = this.baseMapper.selectTenantUserLog(tenantAttendanceParams);
        log.debug("findTenantLog TenantId :{} tenant start:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());
        Tenant tenant = this.baseMapper.selectTenantInfo(tenantAttendanceParams.getTenantId());
        log.debug("findTenantLog TenantId :{} attendance start:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());
        list.parallelStream().forEach(t -> {
            //tenant :name as tenantName,
            t.setTenantName(tenant.getName());
            //tenant_teams_user:teams_user_id,teams_display_name
            List<Users> users = this.baseMapper.selectTeamsUserInfo(t);
            if (!CollectionUtils.isEmpty(users)) {
                if (users.size() == 1) {
                    t.setTeamsUserId(users.get(0).getTeamsUserId());
                    t.setTeamsDisplayName(users.get(0).getTeamsDisplayName());
                } else {
                    List<Users> activeUsers = users.stream().filter(u -> !ObjectUtils.isEmpty(u.getSituation()) && u.getSituation().equalsIgnoreCase("active")).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(activeUsers)) {
                        t.setTeamsUserId(activeUsers.get(0).getTeamsUserId());
                        t.setTeamsDisplayName(activeUsers.get(0).getTeamsDisplayName());
                    } else {
                        t.setTeamsUserId(users.get(0).getTeamsUserId());
                        t.setTeamsDisplayName(users.get(0).getTeamsDisplayName());
                    }

                }

            } else {
                log.debug("findTenantLog not find teamsuser tenant: {} userid:{}", t.getTenantId(), t.getId());
            }
            //tenant_attendance:log_time,timezone,timezone_offset,user_id,action,sub_action,create_time
            TenantAttendance lastattendance = this.baseMapper.selectLastAttendance(t);
            if (!ObjectUtils.isEmpty(lastattendance)) {
                t.setLogTime(lastattendance.getLogTime());
                t.setTimezone(lastattendance.getTimezone());
                t.setTimezoneOffset(lastattendance.getTimezoneOffset());
                t.setUserId(lastattendance.getUserId());
                t.setAction(lastattendance.getAction());
                t.setSubAction(lastattendance.getSubAction());
                t.setCreateTime(lastattendance.getCreateTime());
            } else {
                log.debug("findTenantLog not find tenant_attendance tenant: {} ,userid:{}", t.getTenantId(), t.getId());
            }

        });
        log.debug("findTenantLog TenantId :{} attendance end:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());

        List<TenantAttendance> newlist = new ArrayList<>();
        if (!ObjectUtils.isEmpty(tenantAttendanceParams.getTeam())) {

            if (tenantAttendanceParams.getTeam().toLowerCase().equals("ungrouped")) {
                for (TenantAttendance ta : list) {
                    TenantAttendance tenantAttendance = new TenantAttendance();
                    tenantAttendance.setTenantId(tenantAttendanceParams.getTenantId());
                    tenantAttendance.setUserId(ta.getId());

                    List<String> teamNames = this.baseMapper.selectTenantUserExtend(tenantAttendance);
                    Set<String> set = dealWithTeam(teamNames);
                    String teamStr = String.valueOf(new ArrayList<String>(set));
                    if (ObjectUtils.isEmpty(teamStr) || teamStr.equals("[]") || teamStr.equals("{}")) {
                        newlist.add(ta);
                    }
                }
            } else {
                for (TenantAttendance ta : list) {
                    TenantAttendance tenantAttendance = new TenantAttendance();
                    tenantAttendance.setTenantId(tenantAttendanceParams.getTenantId());
                    tenantAttendance.setUserId(ta.getId());

                    List<String> teamNames = this.baseMapper.selectTenantUserExtend(tenantAttendance);
                    Set<String> set = dealWithTeam(teamNames);
                    String teamStr = String.valueOf(new ArrayList<String>(set));
                    if (!ObjectUtils.isEmpty(teamStr) && teamStr.toLowerCase().contains(tenantAttendanceParams.getTeam().toLowerCase())) {
                        newlist.add(ta);
                    }
                }
            }
        } else {
            newlist = list;
        }
        log.debug("findTenantLog TenantId :{} team end:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());

        AttendanceQueryParam aqp = new AttendanceQueryParam();

        aqp.setTenantId(tenantAttendanceParams.getTenantId());

        aqp.setKey("mspbots.sync.wise.site");
        String site = this.baseMapper.selectSite(aqp);
        site = ObjectUtils.isEmpty(site) ? "mspbots.sync.wise.site" : site;
        String cwUrl = "https://{mspbots.sync.wise.site}/v4_6_release/ConnectWise.aspx?routeTo=ServiceFV&recid="
                .replace("{mspbots.sync.wise.site}", site);

        log.debug("site:{}", site);

        aqp.setKey("mspbots.cw.ticket.working");
        String workingStr = this.baseMapper.selectSite(aqp);
        workingStr = ObjectUtils.isEmpty(workingStr) ? "progress" : workingStr;
        workingStr = workingStr.toLowerCase().trim();
        log.debug("workingStr:{}", workingStr);
        String working = workingStr;
        aqp.setWorking(working);

        log.debug("findTenantLog TenantId :{} time out end:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());


        newlist.stream().forEach(att -> {
            if (!ObjectUtils.isEmpty(att.getCreateTime())) {
                att.setDuration(Duration.between(att.getLogTime(), TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime()).toMinutes());
            } else {
                att.setDuration(Long.valueOf(0));
            }
            //time out
            if (ObjectUtils.isEmpty(att.getAction()) && ObjectUtils.isEmpty(att.getSubAction())) {
                att.setTimeout(false);
            } else {
                if (ObjectUtils.isEmpty(att.getSubAction())) {
                    if (att.getAction().toLowerCase().trim().equals("in")) {
                        if (att.getDuration() > 12 * 60) {
                            att.setTimeout(true);
                        } else {
                            att.setTimeout(false);
                        }
                    } else {
                        att.setTimeout(false);
                    }

                } else {
                    if (att.getSubAction().toLowerCase().trim().equals("lunch")) {
                        if (att.getDuration() > 2 * 60) {
                            att.setTimeout(true);
                        } else {
                            att.setTimeout(false);
                        }

                    } else if (att.getSubAction().toLowerCase().trim().equals("break")) {
                        if (att.getDuration() > 1 * 60) {
                            att.setTimeout(true);
                        } else {
                            att.setTimeout(false);
                        }
                    } else if (att.getSubAction().toLowerCase().trim().equals("back")) {
                        if (att.getDuration() > 12 * 60) {
                            att.setTimeout(true);
                        } else {
                            att.setTimeout(false);
                        }
                    } else {
                        att.setTimeout(false);
                    }
                }
            }


            //cw
//            log.debug("findTicketInfo.aqp: {},working:{}",aqp,working);
//            TicketInfo info = findTicketInfoTest(att,working,cwUrl);
//            att.setTicketInfo(info);
            att.setIdentifier(findUserIdentifier(att));
        });

        log.debug("findTenantLog TenantId :{} auditors start:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());


        List<TicketAuditor> userAuditors = this.baseMapper.selectUserAuditorsStatus(aqp);

        newlist.stream().forEach(att -> {
            TicketInfo info = new TicketInfo();
            String identifier = att.getIdentifier();
            if (ObjectUtils.isEmpty(identifier)) {
                info.setIsCw(false);
                info.setIsTicket(false);
                info.setIsIdle(true);
                info.setTime(0 + "");
            } else {
                info.setIsCw(true);
                List<TicketAuditor> singleUserAuditors = userAuditors.stream().filter(aud -> aud.getUpdatedBy().equalsIgnoreCase(identifier)).collect(Collectors.toList());

                if (CollectionUtils.isEmpty(singleUserAuditors)) {
                    info.setIsTicket(false);
                    info.setIsIdle(true);
                    info.setTime(0 + "");
                } else {
                    info.setIsTicket(true);
                    Boolean idle = true;
                    for (int i = 0; i < singleUserAuditors.size(); i++) {
                        if (ObjectUtils.isEmpty(singleUserAuditors.get(i).getCurrentStatus())) {
                            TicketAuditor progressAuditor = singleUserAuditors.get(i);
                            idle = false;
                            info.setIsIdle(false);
                            info.setCwId(progressAuditor.getTicketId());
                            info.setCwUrl(cwUrl + progressAuditor.getTicketId());
                            info.setColor(progressAuditor.getColor());
                            info.setPriorityName(progressAuditor.getName());
                            info.setTenantId(aqp.getTenantId());
                            LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime();
                            String timeStr = formatTimeStr(Duration.between(progressAuditor.getStartTime(), current).toMinutes());
                            info.setTime(timeStr);
                            break;
                        }
                    }

                    if (idle) {
                        info.setIsIdle(true);
                        LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime();

                        String timeStr = formatTimeStr(Duration.between(singleUserAuditors.get(0).getCurTime(), current).toMinutes());
                        info.setTime(timeStr);
                    }
                }
            }

            att.setTicketInfo(info);
        });

        log.debug("findTenantLog TenantId :{} auditors end time:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());

        log.debug("newlist:{}", newlist.size());

        List<TenantAttendance> lunchList = newlist.stream()
                .filter(t -> !ObjectUtils.isEmpty(t.getSubAction()) && t.getSubAction().toLowerCase().trim().equals("lunch") && !t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> breakList = newlist.stream()
                .filter(t -> !ObjectUtils.isEmpty(t.getSubAction()) && t.getSubAction().toLowerCase().trim().equals("break") && !t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> backList = newlist.stream()
                .filter(t -> !ObjectUtils.isEmpty(t.getSubAction()) && t.getSubAction().toLowerCase().trim().equals("back") && !t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> inList = newlist.stream()
                .filter(t -> ObjectUtils.isEmpty(t.getSubAction()) && !ObjectUtils.isEmpty(t.getAction()) && t.getAction().toLowerCase().trim().equals("in") && !t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> outList = newlist.stream()
                .filter(t -> ObjectUtils.isEmpty(t.getSubAction()) && !ObjectUtils.isEmpty(t.getAction()) && t.getAction().toLowerCase().trim().equals("out"))
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());

        List<TenantAttendance> lunchTimeoutList = newlist.stream()
                .filter(t -> !ObjectUtils.isEmpty(t.getSubAction()) && t.getSubAction().toLowerCase().trim().equals("lunch") && t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> breakTimeoutList = newlist.stream()
                .filter(t -> !ObjectUtils.isEmpty(t.getSubAction()) && t.getSubAction().toLowerCase().trim().equals("break") && t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> inTimeoutList = newlist.stream()
                .filter(t -> ObjectUtils.isEmpty(t.getSubAction()) && !ObjectUtils.isEmpty(t.getAction()) && t.getAction().toLowerCase().trim().equals("in") && t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());
        List<TenantAttendance> backTimeoutList = newlist.stream()
                .filter(t -> !ObjectUtils.isEmpty(t.getSubAction()) && t.getSubAction().toLowerCase().trim().equals("back") && t.getTimeout())
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());

        List<TenantAttendance> emptyList = newlist.stream()
                .filter(t -> ObjectUtils.isEmpty(t.getSubAction()) && ObjectUtils.isEmpty(t.getAction()))
                .sorted(Comparator.comparing(TenantAttendance::getDuration))
                .collect(Collectors.toList());

//        Collections.reverse(lunchList);
//        Collections.reverse(breakList);
//        Collections.reverse(backList);
//        Collections.reverse(inList);
//        Collections.reverse(outList);

        List<TenantAttendance> resultList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(lunchList)) {
            resultList.addAll(lunchList);
        }
        if (!CollectionUtils.isEmpty(breakList)) {
            resultList.addAll(breakList);
        }
        if (!CollectionUtils.isEmpty(backList)) {
            resultList.addAll(backList);
        }
        if (!CollectionUtils.isEmpty(inList)) {
            resultList.addAll(inList);
        }
        if (!CollectionUtils.isEmpty(outList)) {
            resultList.addAll(outList);
        }

        if (!CollectionUtils.isEmpty(lunchTimeoutList)) {
            resultList.addAll(lunchTimeoutList);
        }
        if (!CollectionUtils.isEmpty(breakTimeoutList)) {
            resultList.addAll(breakTimeoutList);
        }
        if (!CollectionUtils.isEmpty(backTimeoutList)) {
            resultList.addAll(backTimeoutList);
        }
        if (!CollectionUtils.isEmpty(inTimeoutList)) {
            resultList.addAll(inTimeoutList);
        }


        if (!CollectionUtils.isEmpty(emptyList)) {
            resultList.addAll(emptyList);
        }

        log.debug("findTenantLog TenantId :{} index end time:{}", tenantAttendanceParams.getTenantId(), LocalDateTime.now());
        log.debug("resultList: {} ", resultList.size());

        return resultList;
    }


    private TicketInfo findTicketInfo(TenantAttendance att, String working, String cwUrl) {

        TicketInfo info = new TicketInfo();

        AttendanceQueryParam aqp = new AttendanceQueryParam();

        aqp.setTenantId(att.getTenantId());

        String identifier = this.baseMapper.selectUserIdentifier(att);
        if (ObjectUtils.isEmpty(identifier)) {
            identifier = this.baseMapper.selectUserMappingIdentifier(att);
        }
        log.info("ticketInfo::identifier:{}", identifier);
        if (ObjectUtils.isEmpty(identifier)) {
            info.setIsCw(false);
            info.setIsTicket(false);
            info.setIsIdle(true);
            info.setTime(0 + "");
        } else {
            info.setIsCw(true);
            aqp.setIdentifier(identifier);
//            TicketAuditor auditors = this.baseMapper.selectUserAuditor(aqp);
            List<TicketInfo> tickets = this.baseMapper.selectUserTickets(aqp);
            log.info("ticketInfo::tickets:{}", tickets.size());
            if (CollectionUtils.isEmpty(tickets)) {
                info.setIsTicket(false);
                info.setIsIdle(true);
                info.setTime(0 + "");
            } else {
                info.setIsTicket(true);
                Boolean isIdle = true;
                for (TicketInfo ticketInfo : tickets) {
                    log.debug("ticketInfo.getStatus():{}", ticketInfo.getStatus().trim().toLowerCase());
                    log.debug("ticketInfo::working:{}", working);
                    if (ticketInfo.getStatus().trim().toLowerCase().contains(working)) {
                        aqp.setCwId(ticketInfo.getCwId());
                        TicketAuditor lastAuditor = this.baseMapper.selectUserAuditor(aqp);
                        String updateBy = identifier;
                        if (!ObjectUtils.isEmpty(lastAuditor)) {
                            updateBy = lastAuditor.getUpdatedBy();
                        }
                        log.debug("updateBy:{}", updateBy);
                        if (updateBy.equalsIgnoreCase(identifier)) {
                            info.setCwId(ticketInfo.getCwId());
                            isIdle = false;

                            for (int i = ticketInfo.getAuditor().size(); i >= 0; i--) {
                                JsonNode auditor = ticketInfo.getAuditor().get(i - 1);
                                if (auditor.get("name").asText().trim().toLowerCase().contains(working)) {
                                    auditor.get("time").asText();
                                    log.debug("ticket tenantId {},cwId {},auditor id {}", ticketInfo.getTenantId(), ticketInfo.getCwId(), auditor.get("id").asText());
                                    LocalDateTime time = LocalDateTime.parse(auditor.get("time").asText(), DateTimeFormatter.ISO_DATE_TIME);
                                    LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime();
                                    log.debug("ticketInfo time:" + time);
                                    log.debug("ticketInfo current:" + current);
                                    info.setTime(formatTimeStr(Duration.between(time, current).toMinutes()));
                                    break;
                                }
                            }

                            info.setPriorityName(ticketInfo.getPriorityName());
                            info.setColor(ticketInfo.getColor());

                            info.setCwUrl(cwUrl + ticketInfo.getCwId());
                            break;
                        }
                    }
                }
                if (isIdle) {

                    LocalDateTime time = null;

                    for (TicketInfo ticketInfo : tickets) {
                        aqp.setIdentifier(identifier);
                        aqp.setCwId(ticketInfo.getCwId());
                        List<TicketAuditor> userAuditors = this.baseMapper.selectSingleUserAuditors(aqp);
                        if (!CollectionUtils.isEmpty(userAuditors)) {
                            for (int i = 0; i < userAuditors.size(); i++) {
                                if (userAuditors.get(i).getStatusName().trim().toLowerCase().contains(working) && i - 1 >= 0) {
                                    TicketAuditor nextAuditor = userAuditors.get(i - 1);
                                    LocalDateTime tempTime = nextAuditor.getUpdatedTime();
                                    if (ObjectUtils.isEmpty(time)) {
                                        time = tempTime;
                                    } else {
                                        if (tempTime.isAfter(time)) {
                                            time = tempTime;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (ObjectUtils.isEmpty(time)) {
                        info.setIsCw(true);
                        info.setIsTicket(true);
                        isIdle = true;
                        info.setTime(0 + "");
                    } else {
                        LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime();
                        String timeStr = formatTimeStr(Duration.between(time, current).toMinutes());
//                      log.debug("ticket isIdle:: tenantId {},cwId {},auditor id {},timeStr {}, time :{},current: {}",
//                            ticketIdleInfo.getTenantId(),ticketIdleInfo.getCwId(),lastAuditor.get("id").asText(),timeStr,time,current);
                        info.setTime(timeStr);

                    }

                }

                info.setIsIdle(isIdle);
            }
        }

        return info;
    }

    private String findUserIdentifier(TenantAttendance att) {
        String identifier = this.baseMapper.selectUserIdentifier(att);
        if (ObjectUtils.isEmpty(identifier)) {
            identifier = this.baseMapper.selectUserMappingIdentifier(att);
        }
        return identifier;
    }


    private TicketInfo findTicketInfoTest(TenantAttendance att, String working, String cwUrl) {
        AttendanceQueryParam aqp = new AttendanceQueryParam();

        aqp.setTenantId(att.getTenantId());

        TicketInfo info = new TicketInfo();
        String identifier = this.baseMapper.selectUserIdentifier(att);
        if (ObjectUtils.isEmpty(identifier)) {
            identifier = this.baseMapper.selectUserMappingIdentifier(att);
        }
        if (ObjectUtils.isEmpty(identifier)) {
            info.setIsCw(false);
            info.setIsTicket(false);
            info.setIsIdle(true);
            info.setTime(0 + "");

        } else {
            info.setIsCw(true);
            aqp.setWorking(working);
            aqp.setIdentifier(identifier);
//            List<TicketAuditor> userAuditors = this.baseMapper.selectUserAuditorsStatus(aqp);
            List<TicketAuditor> userAuditors = this.baseMapper.selectUserAuditorsList(aqp);
            if (CollectionUtils.isEmpty(userAuditors)) {
                info.setIsTicket(false);
                info.setIsIdle(true);
                info.setTime(0 + "");

            } else {
//                Boolean idle = true;
//                for(int i = 0; i< userAuditors.size();i++){
//                    if(ObjectUtils.isEmpty(userAuditors.get(i).getCurrentStatus())){
//                        TicketAuditor progressAuditor = userAuditors.get(i);
//                        idle = false;
//                        info.setIsIdle(false);
//                        info.setCwId(progressAuditor.getTicketId());
//                        info.setCwUrl(cwUrl+progressAuditor.getTicketId());
//                        info.setColor(progressAuditor.getColor());
//                        info.setPriorityName(progressAuditor.getName());
//                        info.setTenantId(aqp.getTenantId());
//                        LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(),"UTC").toLocalDateTime();
//                        String timeStr = formatTimeStr(Duration.between(progressAuditor.getStartTime(),current).toMinutes());
//                        info.setTime(timeStr);
//                        break;
//                    }
//                }
//
//                if(idle){
//                    info.setIsIdle(true);
//                    LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(),"UTC").toLocalDateTime();
//                    String timeStr = formatTimeStr(Duration.between(userAuditors.get(0).getCurTime(),current).toMinutes());
//                    info.setTime(timeStr);
//                }


                TicketAuditor last = userAuditors.get(0);
                if (last.getStatusName().trim().toLowerCase().contains(working)) {
                    info.setIsIdle(false);
                    info.setCwId(last.getTicketId());
                    info.setCwUrl(cwUrl + last.getTicketId());
                    info.setColor(last.getColor());
                    info.setPriorityName(last.getName());
                    info.setTenantId(aqp.getTenantId());
                    LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime();
                    String timeStr = formatTimeStr(Duration.between(last.getUpdatedTime(), current).toMinutes());
                    info.setTime(timeStr);
                } else {
                    info.setIsIdle(true);
                    TicketAuditor next = null;
                    for (int i = 0; i < userAuditors.size(); i++) {
                        if (userAuditors.get(i).getStatusName().trim().toLowerCase().contains(working) && i - 1 >= 0) {
                            next = userAuditors.get(i - 1);
                            break;
                        }
                    }

                    if (ObjectUtils.isEmpty(next)) {
                        info.setIsCw(true);
                        info.setIsTicket(true);
                        info.setIsIdle(true);
                        info.setTime(0 + "");
                    } else {
                        LocalDateTime current = TimeZoneUtils.utcToZone(LocalDateTime.now(), "UTC").toLocalDateTime();
                        String timeStr = formatTimeStr(Duration.between(next.getUpdatedTime(), current).toMinutes());
                        info.setTime(timeStr);
                    }

                }

            }

        }

        return info;
    }


    private String formatTimeStr(Long durtion) {
        String minute = "m";
        String hour = "h ";
        String result = "";
        if (durtion < 60) {
            result = durtion + minute;
        } else {
            Long hourNumber = durtion / 60;
            Long minuteNumber = durtion % 60;
            result = hourNumber + hour;
            if (minuteNumber != 0) {
                result = result + minuteNumber + minute;
            }
        }
        return result;
    }

    @Override
    public TenantAttendance findInLog(TenantAttendance tenantAttendance) {
        List<TenantAttendance> list = this.findCurrentDayLog(tenantAttendance);
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public List<Users> findTenantUser(TenantAttendanceController.TenantAttendanceParams params) {
        List<Users> list = this.baseMapper.selectTenantUser(params);
        list.parallelStream().forEach(u -> {
            String name = u.getUsername().split("@")[0];
            if (!ObjectUtils.isEmpty(name) && name.contains(".")) {
                name.replace(".", " ");
            }
            u.setUsername(name);
            if (ObjectUtils.isEmpty(u.getFirstName())) {
                u.setFirstName("");
            }
        });
        list.sort(Comparator.comparing(Users::getFirstName));
        return list;
    }

    @Override
    public Integer commandRegix(String regexStr) {
        regexStr = regexStr.toLowerCase();
        Integer result = 0;
        //in
        //in at/@ am/pm 10:00 bacase xxxx
        String pattern = "^(in|bod)\\s+(at|@)\\s+(([01]?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*[a|p]m\\s+because[\\w\\W]*";
//
        boolean inBecaseMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(in|bod)\\s+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*[a|p]m";
        boolean inAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(in|bod)\\s+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*";
        boolean inOnlyTimeAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(in|bod)(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*";
        boolean inOnlyBlackTimeAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(in|bod)+(at|@)+(((0|1)?\\d)|(2[0-3])):[0-5]";
        boolean inNoBlackAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(in|bod)\\s?(at|@)\\s?(((0|1)?\\d)|(2[0-3])):[0-5]\\d";
        boolean inBlackAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(in|bod)$";
        boolean inOnlyMatch = Pattern.matches(pattern, regexStr);

        result = inNoBlackAtMatch || inOnlyBlackTimeAtMatch || inBecaseMatch || inAtMatch || inOnlyMatch || inOnlyTimeAtMatch || inBlackAtMatch ? 1 : result;

        //out
        pattern = "^(out|eod)\\s+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*[a|p]m\\s+because[\\w\\W]*";
        boolean outBecaseMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(out|eod)\\s+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*[a|p]m";
        boolean outAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(out|eod)\\s+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*";
        boolean outOnlyTimeAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(out|eod)+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*";
        boolean outOnlyBlackTimeAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(out|eod)+(at|@)+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*";
        boolean outNoBlackAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(out|eod)\\s+(at|@)\\s+(((0|1)?\\d)|(2[0-3])):[0-5]\\w[\\w\\W]*";
        boolean outBlackAtMatch = Pattern.matches(pattern, regexStr);
        pattern = "^(out|eod)$";
        boolean outOnlyMatch = Pattern.matches(pattern, regexStr);

        result = outNoBlackAtMatch || outOnlyBlackTimeAtMatch || outBecaseMatch || outAtMatch || outOnlyMatch || outOnlyTimeAtMatch || outBlackAtMatch ? 2 : result;

        //break
        pattern = "^break$";

        boolean breakMatch = Pattern.matches(pattern, regexStr);
        result = breakMatch ? 3 : result;

        //lunch
        pattern = "^lunch$";

        boolean lunchMatch = Pattern.matches(pattern, regexStr);
        result = lunchMatch ? 4 : result;

        //back
        pattern = "^back$";

        boolean backMatch = Pattern.matches(pattern, regexStr);

        result = backMatch ? 5 : result;

        //help
        pattern = "^help$";

        boolean helpMatch = Pattern.matches(pattern, regexStr);

        result = helpMatch ? 6 : result;

        //EODcheck
        pattern = "^(stats|qa)$";

        boolean EodMatch = Pattern.matches(pattern, regexStr);

        result = EodMatch ? 7 : result;

        return result;
    }

    @Override
    public TenantAttendance findAction(TenantAttendanceController.TenantAttendanceParams params) {
        return this.getOne(Wrappers.<TenantAttendance>lambdaQuery()
                .allEq(params.toMap(), false)
                .between(!ObjectUtils.isEmpty(params.getCreateTimeStart()), TenantAttendance::getLogTime,
                        ObjectUtils.isEmpty(params.getCreateTimeStart()) ? LocalDateTime.now() : params.getCreateTimeStart(),
                        ObjectUtils.isEmpty(params.getCreateTimeEnd()) ? LocalDateTime.now() : params.getCreateTimeEnd())
                .orderByDesc(TenantAttendance::getLogTime).last("limit 1"));
    }

    @Override
    public List<TenantAttendance> findLastIn(long tenantId) {
        return this.baseMapper.findLastIn(tenantId);
    }

    @Override
    public List<TenantAttendance> findSubAction(long tenantId) {
        return this.baseMapper.findSubAction(tenantId);
    }
}
